/*

 Copyright (C) 2007 Thomas Geiger (tom.geiger@gmail.com)

 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
 License as published by the Free Software Foundation; either
 version 2.1 of the License, or (at your option) any later version.

 This library is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 Lesser General Public License for more details.

 You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

 */

#include "XIOClient.h"

#include "CredManager.h"

namespace globus_xio {

XIOClient::XIOClient() {
	globus_module_activate(GLOBUS_XIO_MODULE);
	globus_xio_stack_init(&stack, NULL);
	globus_xio_attr_init(&attr);
}

XIOClient::~XIOClient() {
	// destroy attr
	globus_xio_attr_destroy(attr);
	globus_module_deactivate(GLOBUS_XIO_MODULE);
}

bool XIOClient::receive(ostream& os) {
	
	if (url == NULL) {
		cout << "No URL set." << endl;
		return false;
	}
	
	error = false;
	
	char buffer[BUF_SIZE];
	globus_size_t nrOfBytes;

	globus_result_t res;

	res = globus_xio_handle_create(&handle, stack);
	test_error(res, __LINE__);
	res = globus_xio_open(handle, const_cast<char*>(url->getURL().c_str() ), attr);
	test_error(res, __LINE__);

	do {
		res = globus_xio_read(handle, (globus_byte_t*) buffer, sizeof(buffer) - 1, 1,&nrOfBytes, NULL);
        if(nrOfBytes > 0)
        {
            buffer[nrOfBytes] = '\0';
            os << buffer;
        }
    } while (res == GLOBUS_SUCCESS);
	
    globus_xio_close(handle, NULL);

    return !error;
}


bool XIOClient::send(istream& is) 
{	
	if (url == NULL) {
		cout << "No URL set." << endl;
		return false;
	}
	
	error = false;
	
	globus_size_t nrOfBytes = 0;	
	globus_result_t res;

	// determine size of stream
	is.seekg(0, ios::end);
  	int size = is.tellg();  	
  	is.seekg(0, ios::beg);
  	
  	if (size < 1) {
  		return false;
  	}

	/// TODO: split buffer to a smaller size
	char* buffer = new char[size];
	is.read(buffer, size);

	/// todo: try to enable http post
	if (url->getScheme() == "http" || url->getScheme() == "https") {
		cerr << "http/s post not supported." << endl;
		return false;
	} else {
				
		res = globus_xio_handle_create(&handle, stack);
		test_error(res, __LINE__);
	    res = globus_xio_open(handle, const_cast<char*>( url->getURL().c_str() ), NULL);
	    test_error(res, __LINE__);
	
		// send it out there
	    res = globus_xio_write(handle,
	                     (globus_byte_t*) buffer,
				         size, 
				         1, 
				         &nrOfBytes, 
				         NULL);
		
		test_error(res, __LINE__);
		
		// close handle    
	    globus_xio_close(handle, NULL);
        
	}

    delete[] buffer;

    return true;
}

void XIOClient::setUrl(URL* newUrl) 
{
	url = newUrl;	
	initDriver(url);
}
		
URL* XIOClient::getUrl() 
{
	return url;
}	


void XIOClient::initDriver(URL* url) 
{			
	globus_result_t res;
	
	string schema = url->getScheme();
	
	// the following code snipped from the xio list 
	// might be a performance improvement and needs 
	// to be tested
	/*
	globus_reltime_t open_timeout =
	    {
	        90,  // 1.5 minutes
	        0
	    };

	    result = globus_xio_attr_cntl(
	        attr,
	        NULL,
	        GLOBUS_XIO_ATTR_SET_TIMEOUT_OPEN,
	        NULL,
	        &open_timeout,
	        NULL);
	    */
	
	if (schema == "file") {
		
    	res = globus_xio_driver_load("file", &driver);
	    res = globus_xio_stack_push_driver(stack, driver);
	    test_error(res, __LINE__);
	    
	} else if (schema == "http") {
		
		globus_xio_driver_t tcp_driver;
	    //globus_xio_driver_t http_driver;
	    
	    res = globus_xio_driver_load("tcp", &tcp_driver);
		res = globus_xio_stack_push_driver(stack, tcp_driver);
		test_error(res, __LINE__);
		res = globus_xio_driver_load("http", &driver);
		res = globus_xio_stack_push_driver(stack, driver);
		test_error(res, __LINE__);

	} else if (schema == "https") {

		globus_xio_driver_t tcp_driver;
	    globus_xio_driver_t gsi_driver;
	    //globus_xio_driver_t http_driver;
	    
	    res = globus_xio_driver_load("tcp", &tcp_driver);
		res = globus_xio_stack_push_driver(stack, tcp_driver);
		test_error(res, __LINE__);
	    res = globus_xio_driver_load("gsi", &gsi_driver);
		res = globus_xio_stack_push_driver(stack, gsi_driver);
		test_error(res, __LINE__);
				
		res = globus_xio_driver_load("http", &driver);
		res = globus_xio_stack_push_driver(stack, driver);
		test_error(res, __LINE__);

	} else if (schema == "ftp") {
		
		res = globus_xio_driver_load("gridftp", &driver);
	    res = globus_xio_stack_push_driver(stack, driver);
	    test_error(res, __LINE__);
	    	
	} else if (schema == "gsiftp") {
		
		res = globus_xio_driver_load("gridftp", &driver);

		gss_cred_id_t cred;
		CredManager::acquireCredential(cred);
		
		res = globus_xio_attr_cntl (  	
				attr,
		     	driver,
		     	GLOBUS_XIO_GSI_SET_CREDENTIAL,
		     	cred);
		
		res = globus_xio_stack_push_driver(stack, driver);
	    test_error(res, __LINE__);

	    CredManager::releaseCredential(cred);
	    
	} else {
		cerr << "unknown schema: " << schema << endl;	
	}
}

void XIOClient::test_error(globus_result_t res, int line)
{
    if(res == GLOBUS_SUCCESS)
    {
        return;
    }
    
	cerr << "Code line: " << line << ", ";
	cerr << "ERROR: " << globus_error_print_friendly( globus_error_peek(res) ) << endl;
	
	// set global error flag
    error = true;
}

}
